package top.chatgqt.SmartChargeStation.demo.mqtt.FacoryMode.factory;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import top.chatgqt.SmartChargeStation.demo.mqtt.FacoryMode.conf.MqttProps;
import top.chatgqt.SmartChargeStation.demo.mqtt.FacoryMode.model.MqttConstants;

/*
 * MQTT 工厂类
 *
 *@EnableIntegration的作用：
 *      让spring boot能扫描到@ServiceActivator
 */
@Configuration
@EnableIntegration
@Slf4j
public class FactoryBuilder {

    @Resource
    private MqttProps mqttProps;


    /*
      为什么要使用工厂模式创建对象
      1. 对象创建和使用分开
      2. 方便维护
     */

    /**
     * 获取mqtt客户端
     */
    public MqttPahoClientFactory getClientFactory() {
        DefaultMqttPahoClientFactory clientFactory = new DefaultMqttPahoClientFactory();

        MqttConnectOptions options = mqttProps.getOptions();
        // 配置连接地址
        options.setServerURIs(new String[]{mqttProps.getHost()});

        clientFactory.setConnectionOptions(options);

        return clientFactory;
    }

    /*
      Spring Integration 的角色

      1. 消息的生产者()
      2. 消息的处理器(MqttPahoMessageHandler): 消息处理器注入消息通道中，对消息进行处理，例如发送消息，接收
      3. 消息通道(MessageChannel):
     */


    /*
        创建接收发送消息通道

        Spring Integration 发送以及接收消息，都需要通过消息通道
        注意： 发送消息和接收消息的通道并不是同一个通道
     */

    /**
     * 发送通道
     */
    @Bean(name = MqttConstants.OUT_CHANNEL)
    public MessageChannel outChannel() {
        return new DirectChannel();
    }

    /**
     * 接收通道
     */
    @Bean(name = MqttConstants.IN_CHANNEL)
    public MessageChannel inChannel() {
        return new DirectChannel();
    }


    /*
        Spring Integration   ===== 发送消息

            1. 建立消息发送通道
            2. 添加@MessagingGateway注解，并制定发送通道
            3. @MessagingGateway 会拦截发送的消息，并将消息投放到指定的发送消息通道
            4. 创建消息处理器
            5. 通过@ServiceActivator，将消息处理器投放到指定消息通道
     */

    /*
        创建消息处理器
        @ServiceActivator: 将消息处理器投放到指定的消息通道
     */
    @Bean
    @ServiceActivator(inputChannel = MqttConstants.OUT_CHANNEL)
    public MqttPahoMessageHandler outHandler() {
        return new MqttPahoMessageHandler(mqttProps.getClientId(), getClientFactory());
    }

    /*
    ==== 接收消息
    1. 创建接收消息的通道
    2. 创建消息处理器
    3. @ServiceActivator: 将消息处理器投放到指定的消息通道
    4. 设置订阅主题的适配器
     */

    // 创建消息处理器
    @Bean
    @ServiceActivator(inputChannel = MqttConstants.IN_CHANNEL)
    public MessageHandler inHandler() {
        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                // 接收消息的业务逻辑
                log.info("receive message: {}", message.getPayload().toString());
            }
        };
    }

    // 订阅主题的适配器
    @Bean
    public MessageProducer getAdaptor() {
        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
                mqttProps.getClientId(), getClientFactory(), mqttProps.getTopic());

        // 设置转换器
        adapter.setConverter(new DefaultPahoMessageConverter());
        // 设置订阅通道
        adapter.setOutputChannel(inChannel());
        return adapter;
    }
}
